#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
接口文档检查程序
检查体育场馆预订系统的API文档完整性和一致性
"""

import os
import sys
import re
import json
import importlib.util
from pathlib import Path

# 颜色输出
class Colors:
    GREEN = '\033[92m'
    RED = '\033[91m'
    YELLOW = '\033[93m'
    BLUE = '\033[94m'
    PURPLE = '\033[95m'
    CYAN = '\033[96m'
    WHITE = '\033[97m'
    BOLD = '\033[1m'
    UNDERLINE = '\033[4m'
    END = '\033[0m'

def print_header(text):
    print(f"\n{Colors.BOLD}{Colors.BLUE}{'='*60}")
    print(f"🔍 {text}")
    print(f"{'='*60}{Colors.END}")

def print_success(text):
    print(f"{Colors.GREEN}✅ {text}{Colors.END}")

def print_warning(text):
    print(f"{Colors.YELLOW}⚠️  {text}{Colors.END}")

def print_error(text):
    print(f"{Colors.RED}❌ {text}{Colors.END}")

def print_info(text):
    print(f"{Colors.CYAN}ℹ️  {text}{Colors.END}")

class APIDocChecker:
    def __init__(self):
        self.project_root = Path(__file__).parent.parent  # 修复路径，指向项目根目录
        self.api_path = self.project_root / "app" / "api" / "v1"
        self.docs_path = self.api_path / "docs"
        self.issues = []
        self.warnings = []
        self.stats = {
            'total_routes': 0,
            'documented_routes': 0,
            'undocumented_routes': 0,
            'doc_files': 0,
            'swagger_decorators': 0
        }

    def check_doc_files_exist(self):
        """检查文档文件是否存在"""
        print_header("检查文档文件结构")
        
        expected_docs = [
            'auth_docs.py',
            'admin_docs.py', 
            'venue_docs.py',
            'activity_docs.py',
            'order_docs.py',
            'payment_docs.py',
            'dashboard_docs.py'
        ]
        
        for doc_file in expected_docs:
            doc_path = self.docs_path / doc_file
            if doc_path.exists():
                print_success(f"文档文件存在: {doc_file}")
                self.stats['doc_files'] += 1
            else:
                print_error(f"文档文件缺失: {doc_file}")
                self.issues.append(f"缺失文档文件: {doc_file}")

    def scan_api_routes(self):
        """扫描API路由文件"""
        print_header("扫描API路由")
        
        route_files = []
        for root, dirs, files in os.walk(self.api_path):
            for file in files:
                if file.endswith('.py') and file != '__init__.py':
                    if 'docs' not in root:  # 排除docs目录
                        route_files.append(Path(root) / file)
        
        print_info(f"发现 {len(route_files)} 个API文件")
        return route_files

    def check_swagger_decorators(self, file_path):
        """检查文件中的Swagger装饰器"""
        try:
            with open(file_path, 'r', encoding='utf-8') as f:
                content = f.read()
            
            # 查找路由定义
            route_pattern = r"@[\w_]+\.route\(['\"]([^'\"]+)['\"][^)]*\)"
            routes = re.findall(route_pattern, content)
            
            # 查找swagger装饰器
            swagger_pattern = r"@swag_from\("
            swagger_decorators = len(re.findall(swagger_pattern, content))
            
            # 查找函数定义
            function_pattern = r"def\s+(\w+)\([^)]*\):"
            functions = re.findall(function_pattern, content)
            
            return {
                'routes': routes,
                'swagger_decorators': swagger_decorators,
                'functions': functions,
                'has_flasgger_import': 'from flasgger import' in content or 'import flasgger' in content
            }
            
        except Exception as e:
            print_error(f"读取文件失败 {file_path}: {e}")
            return None

    def analyze_api_coverage(self):
        """分析API文档覆盖率"""
        print_header("分析API文档覆盖率")
        
        route_files = self.scan_api_routes()
        
        for file_path in route_files:
            relative_path = file_path.relative_to(self.project_root)
            print_info(f"分析文件: {relative_path}")
            
            analysis = self.check_swagger_decorators(file_path)
            if not analysis:
                continue
            
            routes_count = len(analysis['routes'])
            swagger_count = analysis['swagger_decorators']
            
            self.stats['total_routes'] += routes_count
            self.stats['swagger_decorators'] += swagger_count
            
            if routes_count > 0:
                coverage = (swagger_count / routes_count) * 100 if routes_count > 0 else 0
                
                if coverage >= 80:
                    print_success(f"  路由: {routes_count}, Swagger装饰器: {swagger_count}, 覆盖率: {coverage:.1f}%")
                elif coverage >= 50:
                    print_warning(f"  路由: {routes_count}, Swagger装饰器: {swagger_count}, 覆盖率: {coverage:.1f}%")
                else:
                    print_error(f"  路由: {routes_count}, Swagger装饰器: {swagger_count}, 覆盖率: {coverage:.1f}%")
                    self.issues.append(f"{relative_path}: 文档覆盖率较低 ({coverage:.1f}%)")
                
                if not analysis['has_flasgger_import']:
                    print_warning(f"  缺少flasgger导入")
                    self.warnings.append(f"{relative_path}: 缺少flasgger导入")

    def check_doc_consistency(self):
        """检查文档一致性"""
        print_header("检查文档一致性")
        
        # 检查各个文档文件中的常量定义
        doc_files = list(self.docs_path.glob("*.py"))
        
        for doc_file in doc_files:
            if doc_file.name == '__init__.py':
                continue
                
            try:
                with open(doc_file, 'r', encoding='utf-8') as f:
                    content = f.read()
                
                # 检查是否有SIMPLE_DOC定义
                if 'SIMPLE_DOC' in content:
                    print_info(f"{doc_file.name}: 使用了通用文档模板")
                
                # 检查文档常量定义
                doc_constants = re.findall(r'(\w+_DOC)\s*=', content)
                if doc_constants:
                    print_success(f"{doc_file.name}: 定义了 {len(doc_constants)} 个文档常量")
                else:
                    print_warning(f"{doc_file.name}: 未发现文档常量定义")
                    self.warnings.append(f"{doc_file.name}: 可能缺少文档常量")
                
            except Exception as e:
                print_error(f"检查文档文件失败 {doc_file}: {e}")

    def check_swagger_config(self):
        """检查Swagger配置"""
        print_header("检查Swagger配置")
        
        config_file = self.project_root / "app" / "config" / "swagger_config.py"
        if config_file.exists():
            print_success("Swagger配置文件存在")
            
            try:
                with open(config_file, 'r', encoding='utf-8') as f:
                    content = f.read()
                
                # 检查关键配置
                if 'SWAGGER_CONFIG' in content:
                    print_success("发现SWAGGER_CONFIG配置")
                if 'securitySchemes' in content:
                    print_success("配置了安全认证scheme")
                if 'Bearer' in content:
                    print_success("配置了JWT Bearer认证")
                    
            except Exception as e:
                print_error(f"读取Swagger配置失败: {e}")
        else:
            print_error("Swagger配置文件不存在")
            self.issues.append("缺少Swagger配置文件")

    def check_main_app_config(self):
        """检查主应用配置"""
        print_header("检查主应用Swagger配置")
        
        app_init = self.project_root / "app" / "__init__.py"
        if app_init.exists():
            try:
                with open(app_init, 'r', encoding='utf-8') as f:
                    content = f.read()
                
                if 'from flasgger import Swagger' in content:
                    print_success("已导入Flasgger")
                else:
                    print_warning("未发现Flasgger导入")
                
                if 'swagger = Swagger()' in content:
                    print_success("已初始化Swagger实例")
                else:
                    print_warning("未发现Swagger实例初始化")
                
                if 'swagger.init_app(app)' in content:
                    print_success("已将Swagger绑定到应用")
                else:
                    print_warning("未发现Swagger绑定代码")
                    
            except Exception as e:
                print_error(f"检查应用配置失败: {e}")

    def generate_report(self):
        """生成检查报告"""
        print_header("生成检查报告")
        
        # 计算覆盖率
        total_routes = self.stats['total_routes']
        swagger_decorators = self.stats['swagger_decorators']
        coverage = (swagger_decorators / total_routes * 100) if total_routes > 0 else 0
        
        print(f"{Colors.BOLD}📊 统计信息:{Colors.END}")
        print(f"  总API路由数: {total_routes}")
        print(f"  已文档化路由数: {swagger_decorators}")
        print(f"  文档覆盖率: {coverage:.1f}%")
        print(f"  文档文件数: {self.stats['doc_files']}")
        
        if coverage >= 80:
            print_success(f"文档覆盖率优秀 ({coverage:.1f}%)")
        elif coverage >= 60:
            print_warning(f"文档覆盖率良好 ({coverage:.1f}%)")
        else:
            print_error(f"文档覆盖率需要改进 ({coverage:.1f}%)")
        
        # 问题总结
        if self.issues:
            print(f"\n{Colors.BOLD}❌ 发现的问题 ({len(self.issues)}):{Colors.END}")
            for i, issue in enumerate(self.issues, 1):
                print(f"  {i}. {issue}")
        
        if self.warnings:
            print(f"\n{Colors.BOLD}⚠️  警告信息 ({len(self.warnings)}):{Colors.END}")
            for i, warning in enumerate(self.warnings, 1):
                print(f"  {i}. {warning}")
        
        if not self.issues and not self.warnings:
            print_success("✨ 恭喜！未发现任何问题")
        
        # 建议
        print(f"\n{Colors.BOLD}💡 建议:{Colors.END}")
        if coverage < 80:
            print("  • 为未文档化的API路由添加@swag_from装饰器")
        print("  • 定期检查API文档与实际接口的一致性")
        print("  • 访问 http://your-server:5000/apidocs/ 查看在线文档")

def main():
    """主函数"""
    print(f"{Colors.BOLD}{Colors.PURPLE}")
    print("🔍 体育场馆预订系统 - API文档检查程序")
    print("=" * 50)
    print(f"{Colors.END}")
    
    checker = APIDocChecker()
    
    # 执行各项检查
    checker.check_doc_files_exist()
    checker.check_swagger_config()
    checker.check_main_app_config()
    checker.analyze_api_coverage()
    checker.check_doc_consistency()
    
    # 生成报告
    checker.generate_report()
    
    # 返回适当的退出码
    if checker.issues:
        sys.exit(1)
    else:
        sys.exit(0)

if __name__ == "__main__":
    main() 